延續先前己建立的學生資料範例,今天加上學生成績。
StudentView.kt上加上新增及顯示成績畫面vok-orm和許多orm使用方式類似,在這篇文裡不會特別詳細解說,倘若未曾使用過任何jpa、orm,看這篇文章時可先忽略霧裡看花的部份,日後會專篇介紹。
Grade.kt
data class Grade(
    override var id: Long? = null,
    var student_id: Long? = null,
    val description: String? = null,
    var english: Double? = null,
    var math: Double? = null,
    var mandarin:Double? = null,
    var pe: Double? = null
): KEntity<Long>{
    companion object: Dao<Grade, Long>(Grade::class.java)
    val student: Student?
        get() = student_id?.let { Student.getById(student_id!!)} ?: null
}
V02__CreateGrade.sql (命名規格請參考d04)create TABLE Grade(
    id bigint auto_increment PRIMARY KEY,
    mandarin DOUBLE NOT NULL,
    description VARCHAR(50) NOT NULL,
    english DOUBLE NOT NULL,
    math DOUBLE NOT NULL,
    pe DOUBLE NOT NULL,
    student_id bigint not null REFERENCES Student(id)
)
在這張資料表裡除了自動編號的 primary key id外,還有一個 foreign key student_id,由於使用 H2 database,所以程式重啟時,兩個.sql都會執行。倘若使用的是其他資料庫如 MySQL,則 FlyWay 只會執行尚未被執行的.sql。
StudentView.kt
        :
        : 
    private lateinit var student: Student
    private lateinit var gradeDiv: Div
    private val gradeBinder = beanValidationBinder<Grade>()
    private lateinit var addGradeButton: Button
    private val root = ui {
        verticalLayout {
        :
        :
        :
            h2("成績")
            gradeDiv = div()
            p("新增成績")
            textField("學期"){
                bind(gradeBinder).bind(Grade::description)
                placeholder = "第一學期"
            }
            numberField("國文"){
                bind(gradeBinder).bind(Grade::mandarin)
            }
            numberField("英文"){
                bind(gradeBinder).bind(Grade::english)
            }
            numberField("數學"){
                bind(gradeBinder).bind(Grade::math)
            }
            numberField("體育"){
                bind(gradeBinder).bind(Grade::pe)
            }
            addGradeButton = button("新增"){
                onLeftClick { addGrade() }
            }
            editLink = routerLink(VaadinIcon.EDIT,null)
        }
    }
gradeDiv = div() 表示在畫面上新增空的DIV區塊
    private fun addGrade() {
        val grade = Grade()
        if (gradeBinder.validate().isOk && gradeBinder.writeBeanIfValid(grade)){
            grade.student_id = student.id
            grade.save()
            refreshGrades()
            gradeBinder.readBean(Grade())
        }
    }
    private fun refreshGrades() {
        gradeDiv.removeAll()
        student.grades.fetch().forEach {
            gradeDiv.html("<p>" +
                    "<strong>學期:</strong>${it.description} " +
                    "<strong>國文:</strong>${it.mandarin} " +
                    "<strong>英文:</strong>${it.english} " +
                    "<strong>數學:</strong>${it.math} " +
                    "<strong>體育:</strong>${it.pe}" +
                    "</p>")
        }
    }
清空DIV區塊,查詢這位學生所有成績後再逐一顯示,這裡插入div區塊使用的是html,在VoK裡,對dom的操作也極為容易。
Student.kt
    val grades : DataLoader<Grade>
        get() = Grade.dataLoader.withFilter {
            Grade::student_id eq id
        }
當有人讀取grads時,下條件查詢資料來源 Grade
Grade.kt加上欄位驗證資訊    @field:NotNull
    var student_id: Long? = null,
    @field:NotNull
    @field:Size(min = 4)
    var description: String? = null,
    @field:NotNull
    @field:DecimalMin(value = "0.0")
    @field:DecimalMax(value = "100.0")
    var english: Double? = null,
    
    :
    :
    (略)
執行結果 :

本日Source Code已上傳 GitHub